package com.ncu.spring.aop.proxy;

import com.ncu.spring.aop.advisor.DefaultMethodInvocation;
import com.ncu.spring.aop.advisor.Interceptor;
import com.ncu.spring.aop.util.AopUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

/**
    JDK动态代理实现
    实现InvocationHandler接口 ：用来对方法进行反射调用
 */
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {

    private ProxyFactory proxyFactory;
    //目标类实现的接口
    private final Class<?>[] proxiedInterfaces;

    public JdkDynamicAopProxy(ProxyFactory proxyFactory) {
        this.proxyFactory = proxyFactory;
        this.proxiedInterfaces = completeProxiedInterfaces(this.proxyFactory);
    }

    @Override //通过Proxy.newProxyInstance()创建代理对象
    public Object getProxy() {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                this.proxiedInterfaces, this);
    }

    @Override //代理方法，代理对象每次执行方法，执行的就是这个方法。
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object oldProxy = null;  //原本ThreadLocal中的代理
        boolean setProxyContext = false;

        TargetSource targetSource = this.proxyFactory.getTargetSource();
        Object target = null;

        try {
            Object retVal;

            // 暴露当前正在运行的代理对象给 AopContext
            if (this.proxyFactory.exposeProxy()) {
                //将原本ThreadLocal中的代理拿出来 并将当前的代理放入
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true; //表示进行了 上面一行的操作
            }

            target = targetSource.getTarget();
            Class<?> targetClass = target.getClass();

            // 得到此 method 的拦截器链，就是一堆环绕通知
            // 需要根据 invoke 的 method 来做进一步确定，过滤出应用在这个 method 上的 Advice
            List<Interceptor> chain = this.proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
            //没有Advice 就直接调用目标方法
            if (chain.isEmpty()) {
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
            } else {
                //有Advice 就创建 调用链
                DefaultMethodInvocation methodInvocation = new DefaultMethodInvocation(target, method, args, chain);
                retVal = methodInvocation.proceed();
            }
            // 处理特殊的返回值 this  如果方法需要的返回值
            Class<?> returnType = method.getReturnType();
            //目标方法的返回值是本身 但是设置返回值类型 是代理类的父类或者父接口
            if (retVal != null && retVal == target &&
                    returnType != Object.class && returnType.isInstance(proxy)) {
                retVal = proxy;
            }
            return retVal;
        } finally {
            //判断是否经历 原本ThreadLocal中的代理拿出并将当前代理放入的操作
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy); //将原来ThreadLocal中代理重新放入
            }
        }
    }

    //获取 目标类实现的接口
    private Class<?>[] completeProxiedInterfaces(ProxyFactory proxyFactory) {
        Class<?>[] proxiedInterfaces = proxyFactory.getProxiedInterfaces();
        return proxiedInterfaces;
    }

}
